package com.zxy.zxyrpc.proxy;

import cn.hutool.core.collection.CollUtil;
import com.zxy.zxyrpc.exception.ConnectException;
import com.zxy.zxyrpc.RpcApplication;
import com.zxy.zxyrpc.config.RpcConfig;
import com.zxy.zxyrpc.exception.RpcException;
import com.zxy.zxyrpc.fault.tolerant.TolerantStrategy;
import com.zxy.zxyrpc.fault.tolerant.TolerantStrategyFactory;
import com.zxy.zxyrpc.loadBalancer.LoadBalancer;
import com.zxy.zxyrpc.loadBalancer.LoadBalancerFactory;
import com.zxy.zxyrpc.model.*;
import com.zxy.zxyrpc.registry.Registry;
import com.zxy.zxyrpc.registry.RegistryFactory;
import com.zxy.zxyrpc.retry.RetryStrategy;
import com.zxy.zxyrpc.retry.RetryStrategyFactory;
import com.zxy.zxyrpc.server.tcp.VertxTcpClient;
import com.zxy.zxyrpc.utils.LogUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * 服务代理
 *
 * @author zxy
 * @since 2025/5/11
 **/
public class ServiceProxy implements InvocationHandler {
    private final ReferenceService referenceService;

    public ServiceProxy(ReferenceService referenceService) {
        this.referenceService = referenceService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 构建请求对象
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest RpcReq = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        // 从注册中心获取请求地址
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());

        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceName(serviceName);
        serviceMetaInfo.setServiceVersion(referenceService.getServiceVersion());

        List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
        if (CollUtil.isEmpty(serviceMetaInfoList)) {
            throw new RpcException(ErrorCode.NOT_FOUND_ERROR, "暂无服务地址");
        }
        // 负载均衡 "服务"
        LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(referenceService.getLoadBalancer());
        // 将方法名作为负载参数
        HashMap<String, Object> reqParams = new HashMap<>();
        reqParams.put("methodName", RpcReq.getMethodName());
        ServiceMetaInfo selectServer = loadBalancer.select(reqParams, serviceMetaInfoList);
        // 发送 TCP 请求(重试策略+容错策略)
        RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(referenceService.getRetryStrategy());
        RpcResponse rpcResponse;
        try {
            rpcResponse = retryStrategy.doRetry(() -> VertxTcpClient.doRequest(RpcReq, selectServer));
        } catch (ConnectException e) {
            // todo 1.重试，建议延迟一下
            // rpcResponse = retryStrategy.doRetry(() -> VertxTcpClient.doRequest(RpcReq, selectServer));
            // 2.立马下线，待优化
            registry.unRegistry(selectServer);
            throw new RpcException(ErrorCode.OPERATION_ERROR, "TCP 连接异常");
        } catch (Exception e) {
            TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(referenceService.getTolerantStrategy());
            reqParams.put("serviceList", serviceMetaInfoList);
            reqParams.put("reqMsg", RpcReq);
            rpcResponse = tolerantStrategy.doTolerant(reqParams, e);
        }
        LogUtils.OkLog(String.format("服务: {} 调用成功",selectServer));
        return rpcResponse.getData();
    }

    /*
    // http 服务代理
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 构建请求对象
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        // 从服务注册中心获取服务提供者的服务地址
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());

        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceName(serviceName);
        serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
        List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());

        if (CollUtil.isEmpty(serviceMetaInfoList)) {
            throw new RuntimeException("暂无服务地址");
        }
        // todo 暂且先获取第一个服务地址（负载均衡）
        ServiceMetaInfo selectSMIL = serviceMetaInfoList.get(0);
        // 指定序列化器
        Serializer serializer = SerializerFactory.getInstance(rpcConfig.getSerializer());
        // 发请求并获得响应
        try {
            byte[] bodyBytes = serializer.serialize(rpcRequest);
            // todo 注意。这里地址被硬编码了（需要使用注册中心/服务发现机制解决） "http://localhost:8080"
            try (HttpResponse httpResponse = HttpRequest.post(selectSMIL.getServiceAddress())
                    .body(bodyBytes)
                    .execute()) {
                byte[] result = httpResponse.bodyBytes();
                RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
                return rpcResponse.getData();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }*/
}
